// ArrayRef.h
// Data structures for array subscript expressions.
#ifndef _SUBSCRIPTEXPR_H_
#define _SUBSCRIPTEXPR_H_

#include <llvm/ADT/APSInt.h>
#include <llvm/Support/raw_ostream.h>
#include <vector>
#include <ostream>

/** Class for a linear subscript expression: coefficients of index variables. */
/* It's really just a way to have an APSInt vector with zero-initialization and addition. */
class SubscriptExpr {

    int _size; // number of induction variables + 1
    std::vector<llvm::APSInt>* _coeffs;

public:
    // If these were const we'd need to to override the default assignment operator.
    int stmtIndex; // which statement this occurs in (if more than 1)
    std::string arrayName;
    // no default constructor - must always create with fixed size.
    
    /** ctor takes the number of indexes and makes a vector of that size + 1. */
    SubscriptExpr(int size, std::string arrayName, int stmtIndex) :
        stmtIndex(stmtIndex),
        arrayName(arrayName)
    {
        _coeffs = new std::vector<llvm::APSInt>(size+1);
        _size = size+1;

        for (int i=0; i < size+1; i++)
            (*_coeffs)[i] = llvm::APSInt(32, false);
    }

    /** copy constructor needed for push_back */
    SubscriptExpr(const SubscriptExpr& expr) :
        stmtIndex(expr.stmtIndex),
        arrayName(expr.arrayName)
    {
        // call the vector's copy constructor.
        _coeffs = new std::vector<llvm::APSInt>(*(expr._coeffs));
        _size = expr._size;
    }

    ~SubscriptExpr() {
        delete _coeffs;
    }

    int size() const {
        return _size;
    }
    
    llvm::APSInt& operator[] (int x) const {
        return (*_coeffs)[x];
    }

    /** Operators for vector addition. */
    SubscriptExpr& operator+= (const SubscriptExpr& rhs) {
        // need to check that size is the same and throw an exception if not.
        for (int i=0; i < _size; i++) {
            (*_coeffs)[i] += rhs[i];
        }
    }
    
    SubscriptExpr operator+ (const SubscriptExpr& rhs) const {
        /* SubscriptExpr result(*this);
           return (result += rhs); */
        SubscriptExpr result(_size - 1, arrayName, stmtIndex);
        for (int i=0; i < _size; i++) {
            result[i] = (llvm::APSInt) ((*_coeffs)[i] + rhs[i]);
        }
        return result;
    }

    SubscriptExpr operator- (const SubscriptExpr& rhs) const {
        /* SubscriptExpr result(*this);
           return (result += rhs); */
        SubscriptExpr result(_size - 1, arrayName, stmtIndex);
        for (int i=0; i < _size; i++) {
            result[i] = (llvm::APSInt) ((*_coeffs)[i] - rhs[i]);
        }
        return result;
    }
    
    /** Operator for output. */
    friend llvm::raw_ostream& operator<< (llvm::raw_ostream& out, const SubscriptExpr& expr);
    
};

#endif
